import json
from urllib import parse
from urllib.request import urlopen
from django.conf import settings
import logging
from .exceptions import OauthQQError
from itsdangerous import TimedJSONWebSignatureSerializer, BadData

logger = logging.getLogger('django')


def get_attr_or_error(instance, attr, errmsg):
    assert hasattr(instance, attr), errmsg
    return getattr(instance, attr)


class OAuthQQ():
    def __init__(self, client_id=None, client_secret=None, redirect_uri=None, state=None):
        self.client_id = client_id or get_attr_or_error(settings, 'QQ_CLIENT_ID', '请在settings文件中添加QQ_CLIENT_ID以获取APPID')
        self.client_secret = client_secret or get_attr_or_error(settings, 'QQ_CLIENT_SECRET',
                                                                '请在配置文件中设置QQ_CLIENT_SECRET以添加秘钥')
        self.redirect_uri = redirect_uri or get_attr_or_error(settings, 'QQ_REDIRECT_URI',
                                                              '请在配置文件中传入QQ_REDIRECT_URI以获取回调网页')
        self.state = state or get_attr_or_error(settings, 'QQ_STATE', '请在配置文件中设置QQ_STATE')

    def get_login_url(self, state):
        # 用户访问QQ登录页的基本url  参数在后面拼接
        url = 'https://graph.qq.com/oauth2.0/authorize?'
        # params中的参数都是接口文档中要求的的必传参数
        params = {
            "response_type": "code",
            "client_id": self.client_id,
            "redirect_uri": self.redirect_uri,
            "state": self.state  # 用于保存qq登录成功之后的跳转页面的路径
        }
        # 此方法将字典转成查询字符串的格式
        data = parse.urlencode(params)
        url += data
        return url

    def get_access_token(self, code):
        url = 'https://graph.qq.com/oauth2.0/token?'
        params = {
            "grant_type": "authorization_code",
            "client_id": self.client_id,
            "client_secret": self.client_secret,
            "code": code,
            "redirect_uri": self.redirect_uri
        }
        url += parse.urlencode(params)
        # 使用urllib的方法请求网页
        resp = urlopen(url)
        resp_data = resp.read().decode()  # 解码之后是查询字符串类型的数据
        data = parse.parse_qs(resp_data)  # 转换程字典
        access_token = data.get('access_token', None)
        if not access_token:
            logger.error('qq登录访问异常')
            raise OauthQQError

        # access_token是一个列表类型的数据,在前端访问的时候会报错,所以要取出来数据
        return access_token[0]

    def get_openid(self, access_token):
        url = 'https://graph.qq.com/oauth2.0/me?'
        params = {
            "access_token": access_token
        }
        url += parse.urlencode(params)
        resp = urlopen(url)
        resp_data = resp.read().decode()
        # callback( {"client_id":"YOUR_APPID","openid":"YOUR_OPENID"} );
        # 返回的包是一个字符串  需要将其中的json字符串切割出来 再进行loads转成字典
        try:
            data = json.loads(resp_data[10:-4])
        except Exception:
            logger.error('获取openid异常')
            raise OauthQQError
        openid = data.get('openid')
        return openid

    @staticmethod
    def generate_user_access_token(openid):
        """生成token的通用方法"""
        sl = TimedJSONWebSignatureSerializer(settings.SECRET_KEY, 300)
        data = sl.dumps({"openid": openid})  # 返回的是bytes类型数据
        return data.decode()

    @staticmethod
    def check_user_access_token(access_token):
        sl = TimedJSONWebSignatureSerializer(settings.SECRET_KEY, 300)
        try:
            data = sl.loads(access_token)  # 返回字典
        except BadData:
            # 解密失败的错误都会被baddata捕获到  此时返回None  序列化器中得不到结果就会停止往后校验
            return None
        else:
            return data.get('openid')
